from types import StringType
from gevent.queue import Queue
from gevent.event import Timeout
from gevent import sleep
import time, os

class GLog(object):
	ENDC = "\033[0m"
	GREEN = "\033[;92m\033[1m"
	GRAY = "\033[1;30m\033[1m"
	RED = "\033[;91m\033[1m"
	BLUE = "\033[;94m\033[1m"
	YELLOW = "\033[;93m\033[1m"
	color_ind = [GRAY + '[I]' + ENDC, RED + '[E]' + ENDC, BLUE + '[D]' + ENDC, YELLOW + '[W]' + ENDC, GREEN + '[SUM]' + ENDC]
	ind = ['[I]', '[E]', '[D]', '[W]', '[SUM]']
	debug = False
	sd = {}
	timeout = 0.0
	stat_loop_sleep = 10.0
	cpu_usage = 0.0

	log_files = {}

	_instance = None
	class LogStd():
		def __init__(self, glog, name, lvl = 3):
			self.name = name
			self.glog = glog
			self.lvl = lvl

		def write(self, msg):
			if len(msg) != 0 and msg != '\n':
				if msg[-1] == '\n':
					msg = msg[:-1]
				self.glog.log(msg, self.name, self.lvl)

		def flush(self):
			pass

	def __new__(cls, *args, **kwargs):
		if not cls._instance:
			cls._instance = super(GLog, cls).__new__(cls, *args, **kwargs)
			cls._instance.q = Queue(1000000)
			cls._instance.s = time.time()
			cls._instance.greenlet1 = None
			cls._instance.greenlet2 = None
		return cls._instance

	def emergency_quit(self):
		from gevent.pool import Pool
		p = Pool(1)
		self.add()
		self.q.put((time.time() - self.s, 9, '', 'q'))
		self.init(p, True, None)
		p.join()

	def add(self, file_name = '/dev/stdout', color = True):
		if file_name not in self.log_files:
			self.log_files[file_name] = (file(file_name, 'a'), color)
			#TODO: if could not open file

	def del_log(self, file_name):
		if file_name in self.log_files:
			del self.log_files[file_name]

	def init(self, pool, devel, config):
		import time
		# redirect standard file descriptors
		import sys, os
		sys.stdout.flush()
		sys.stderr.flush()
		si = file('/dev/null', 'r')
		os.dup2(si.fileno(), sys.stdin.fileno())
		if not devel:
			sys.stderr = self.LogStd(self, 'stderr', 1)
		sys.stdout = self.LogStd(self, 'stdout')

		self.greenlet1 = pool.spawn(self.receiver)
		if config is not None:
			if config.main.stat:
				self.greenlet2 = pool.spawn(self.stat_thread)
			self.log('<' + 20 * '-', '')

	def stat_thread(self):
		self.sd['times'] = os.times()
		import sqlite3
		import cPickle as pickle
		self.db = sqlite3.connect(":memory:")
		c = self.db.cursor()
		c.execute("create table data(id long primary key, data blob)")
		c.close()
		self.db.commit()
		while True:
			sleep(self.stat_loop_sleep)
			o1 = self.sd['times'][0]
			self.sd['times'] = os.times()
			o2 = self.sd['times'][0]
			self.cpu_usage = 100.0 * (o2 - o1) / self.stat_loop_sleep
			s = pickle.dumps(self.sd)
			c = self.db.cursor()
			c.execute('INSERT INTO data VALUES (?,?);', (int(time.time()), s))
			c.close()
			self.db.commit()

	def receiver(self):
		while True:
			(d, l, n, t) = self.q.get()
			if l == 2 and self.debug == False:
				continue
			elif l == 9 and t == 'q':
				break
			elif l == 8:
				if t.ok:
					l = 0
				else:
					l = 1
				#t = t.to_string(color = True)
				t = t.to_short()
				if len(t) == 0:
					continue
		
			for i in self.log_files:
				fd, color = self.log_files[i]
				if color:
					fd.write('%f: %s %s: %s\n' % (d, self.color_ind[l], n, t))
				else:
					fd.write('%f: %s %s: %s\n' % (d, self.ind[l], n, t))
				fd.flush()

	def log(self, text, name, level = 0):
		self.q.put_nowait((time.time() - self.s, level, name, text))

	def loga(self, ans, name, oid = 0):
		if ans.op != 0 and oid != 0:
			if oid in self.sd:
				if ans.op in self.sd[oid]:
					self.sd[oid][ans.op] += 1
				else:
					self.sd[oid][ans.op] = 1
			else:
				self.sd[oid] = {}
				self.sd[oid][ans.op] = 1
		self.q.put_nowait((time.time() - self.s, 8, name, ans))

	def stat(self):
		import cPickle as pickle
		c = self.db.cursor()
		c.execute('select data from data where id>:cur limit 5;', {'cur': int(time.time())-50})
		s = ''
		for row in c:
			s += str(pickle.loads(str(row[0]))) + '\n'
		c.close()
		return '%s\ncurrent:\n%s' % (s, str(self.sd))

	def stop(self):
		self.log(20 * '-' + '>', '')
		self.q.put((time.time() - self.s, 9, '', 'q'))
		if self.greenlet1 is not None: self.greenlet1.kill()
		if self.greenlet2 is not None: self.greenlet2.kill()

class Logged():
	name = ''
	glog = GLog()

	def loga(self, a):
		# log GAnswer object
		name = self.name
		if a.name is not None: name = a.name
		self.glog.loga(a, name, self.oid)

	def _log(self, s):
		self.glog.log(s, self.name)

	def log(self, s):
		self.glog.log(s, self.name)

	def log_error(self, s):
		self.glog.log(s, self.name, 1)

	def log_debug(self, s):
		self.glog.log(s, self.name, 2)

	def log_warn(self, s):
		self.glog.log(s, self.name, 3)

	def log_crit(self, s):
		self.glog.log(s, self.name, 4)
